#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

constexpr int BUFFER_SIZE = 4096;
enum CHECK_STATE {
    CHECK_STATE_REQUESTLINE = 0,
    CHECK_STATE_HEADER
};

enum LINE_STATUS {
    LINE_OK = 0,
    LINE_BAD,
    LINE_OPEN
};

enum HTTP_CODE {
    NO_REQUEST,
    GET_REQUEST,
    BAD_REQUEST,
    FORBIDDEN_REQUEST,
    INTERNAL_ERROR,
    CLOSED_CONNECTION
};

static const char* const szret[] = {"I get the correct result.\n", "Something's wrong.\n"};

LINE_STATUS parse_line(char *buffer, int &check_index, int &read_index) {
    char tmp;
    for (; check_index < read_index; ++check_index) {
        tmp = buffer[check_index];
        if (tmp == '\r') {
            if ((check_index + 1) == read_index) {
                return LINE_OPEN;
            } else if (buffer[check_index + 1] == '\n') {
                buffer[check_index++] = '\0';
                buffer[check_index++] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        } else if (tmp == '\n') {
            if ((check_index > 1) && buffer[check_index - 1] == '\r') {
                buffer[check_index++] = '\0';
                buffer[check_index++] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
    }
    return LINE_OPEN;
}

HTTP_CODE parse_requestline(char *tmp, CHECK_STATE &check_state) {
    char *url = strpbrk(tmp, " \t");    // strpbrk(const char *str1, const char *str2) 检索字符串 str1 中第一个匹配字符串 str2 中字符的字符，返回下标
    if (!url) {
        return BAD_REQUEST;
    }
    *url++ = '\0';// ?
    char *method = tmp;
    if (strcasecmp(method, "GET") == 0) {
        printf("The request method is GET\n");
    } else {
        return BAD_REQUEST;
    }
    url += strspn(url, " \t");  // strpbrk(const char *str1, const char *str2) 检索字符串 str1 中第一个不属于字符串 str2 中字符的字符，返回下标
    char *version = strpbrk(url, " \t");
    if (!version) {
        return BAD_REQUEST;
    }
    *version++ = '\0';
    version += strspn(version, " \t");
    if (strcasecmp(version, "HTTP/1.1") != 0) {
        return BAD_REQUEST;
    }
    if (strncasecmp(url, "http://", 7) == 0) {
        url += 7;
        url = strchr(url, '/');
    }
    if (!url || url[0] != '/') {
        return BAD_REQUEST;
    }
    printf("The request URL is:\n%s\n", url);
    check_state = CHECK_STATE_HEADER;
    return NO_REQUEST;
}

HTTP_CODE parse_headers(char *tmp) {
    if (tmp[0] == '\0') {
        return GET_REQUEST;
    } else if (strncasecmp(tmp, "Host:", 5) == 0) {
        tmp += 5;
        tmp += strspn(tmp, " \t");
        printf("The request host is:\n%s\n", tmp);
    } else {
        printf("I cant handle this header\n");
    }
    return NO_REQUEST;
}

HTTP_CODE parse_content(char *buffer, int &check_index, CHECK_STATE &checkstate, int &read_index, int &start_line) {
    LINE_STATUS linestatus = LINE_OK;
    HTTP_CODE httpcode = NO_REQUEST;
    while ((linestatus = parse_line(buffer, check_index, read_index)) == LINE_OK) {
        char *tmp = buffer + start_line;
        // printf("line: %s, check_index: %d, read_index: %d\n", tmp, check_index, read_index);
        start_line = check_index;
        switch (checkstate) {
            case CHECK_STATE_REQUESTLINE: {
                httpcode = parse_requestline(tmp, checkstate);
                if (httpcode == BAD_REQUEST) {
                    return BAD_REQUEST;
                }
                break;
            }
            case CHECK_STATE_HEADER: {
                httpcode = parse_headers(tmp);
                if (httpcode == BAD_REQUEST) {
                    return BAD_REQUEST;
                } else if (httpcode == GET_REQUEST) {
                    return GET_REQUEST;
                }
            }
            default: {
                return INTERNAL_ERROR;
            }
        }
    }
    if (linestatus == LINE_OPEN) {
        printf("no_request\n");
        return NO_REQUEST;
    } else {
        return BAD_REQUEST;
    }
}

int main(int argc, char **argv) {
    if (argc != 3) {
        printf("usage: %s ip_address port_number backlog\n", basename(argv[0]));
        return 1;
    }
    const char* const ip = argv[1];
    int port = atoi(argv[2]);
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET, ip, &address.sin_addr);
    int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(sockfd >= 0);

    int reuse = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    int ret = bind(sockfd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);
    ret = listen(sockfd, 5);
    assert(ret != -1);

    struct sockaddr_in client;
    socklen_t client_addrlen = sizeof(client);
    int connfd = accept(sockfd, (struct sockaddr*)&client, &client_addrlen);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
    } else {
        char buffer[BUFFER_SIZE];
        memset(buffer, 0, BUFFER_SIZE);
        int data_read = 0;
        int read_index = 0;
        int check_index = 0;
        int start_line = 0;
        CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;
        while(1) {
            data_read = recv(connfd, buffer + read_index, BUFFER_SIZE - read_index, 0);
            for (int i = 0; buffer[i] != '\0'; i++) {
                if (buffer[i] == '\r') {
                    printf("\\r");
                } else if (buffer[i] == '\n') {
                    printf("\\n");
                    printf("%c", buffer[i]);
                } else {
                    printf("%c", buffer[i]);
                }
            }
            printf("\n");
            if (data_read == -1) {
                printf("reading failed\n");
                break;
            } else if (data_read == 0) {
                printf("remote client has closed the connection\n");
                break;
            }
            read_index += data_read;
            HTTP_CODE result = parse_content(buffer, check_index, checkstate, read_index, start_line);
            if (result == NO_REQUEST) {
                continue;
            } else if (result == GET_REQUEST) {
                send(connfd, szret[0], strlen(szret[0]), 0);
                break;
            } else {
                send(connfd, szret[1], strlen(szret[1]), 0);
                break;
            }
            printf("%d\n", result);
        }
        close(connfd);
    }
    close(sockfd);
    return 0;
}